package com.demo.encryptionDecryption.sign;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * 加签与验签demo
 * 参考：
 * https://blog.csdn.net/jay_1989/article/details/52789617
 * https://www.cnblogs.com/gdayq/p/5955756.html
 * @author qingqing.liang
 * @create 2018-04-27
 **/
public class Client {
    static String TEST_URL = "测试URL";
    static String TEST_KEY = "测试KEY";
    static String TEST_SEC = "测试SEC";

    public static void main(String[] args) throws UnsupportedEncodingException, NoSuchAlgorithmException {
//        String result = getResult(TEST_URL, getReqParam());
//        System.out.print(result);

        String paramStr =  getReqParam();
        verifySign(paramStr);
    }

    /**
     * 请求参数
     * @return
     * @throws UnsupportedEncodingException
     * @throws NoSuchAlgorithmException
     */
    private static String getReqParam() throws UnsupportedEncodingException, NoSuchAlgorithmException {
        TreeMap<String, String> req = new TreeMap<String, String>();
        req.put("a", TEST_KEY);
        req.put("f", "json");
        req.put("l", "zh_CN");
        req.put("m", "zhongan.repair.query");
        req.put("v", "1.0");
        req.put("i", "" + System.currentTimeMillis() / 1000);
        req.put("params", "{\"assignNo\":\"TEST018\"}");
        req.put("s", sign(req, null, TEST_SEC));

        StringBuilder param = new StringBuilder();
        for (Iterator<Map.Entry<String, String>> it = req.entrySet().iterator(); it.hasNext();) {
            Map.Entry<String, String> e = it.next();
            param.append("&").append(e.getKey()).append("=").append(URLEncoder.encode(e.getValue(), "UTF-8"));
        }

        String s =  param.toString().substring(1);
        System.out.println("请求参数：" + s);
        return s;
    }

    /**
     * 返回签名结果
     * @param paramValues 参数
     * @param ignoreParamNames 忽略的参数
     * @param secret 秘钥之类的东西
     * @return
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     */
    private static String sign(Map<String, String> paramValues, List<String> ignoreParamNames, String secret) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        StringBuilder sb = new StringBuilder();
        List<String> paramNames = new ArrayList<String>(paramValues.size());
        paramNames.addAll(paramValues.keySet());
        if (ignoreParamNames != null && ignoreParamNames.size() > 0) {
            for (String ignoreParamName : ignoreParamNames) {
                paramNames.remove(ignoreParamName);
            }
        }
        Collections.sort(paramNames);

        sb.append(secret);
        for (String paramName : paramNames) {
            sb.append(paramName).append(paramValues.get(paramName));
        }
        sb.append(secret);

        MessageDigest md = MessageDigest.getInstance("SHA-1");
        System.out.println("待生成摘要字符串：" + sb.toString());
        String digest = byte2hex(md.digest(sb.toString().getBytes("UTF-8")));
        System.out.println("摘要：" + digest);
        return digest;
    }

    /**
     * 字节数组转16进制
     * @param bytes
     * @return
     */
    private static String byte2hex(byte[] bytes) {
        StringBuilder sign = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if (hex.length() == 1) {
                sign.append("0");
            }
            sign.append(hex.toUpperCase());
        }
        return sign.toString();
    }

    /**
     * 获取请求结果
     * @param urlStr
     * @param content
     * @return
     */
    private static String getResult(String urlStr, String content) {
        URL url = null;
        HttpURLConnection connection = null;
        try {
            url = new URL(urlStr);
            connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
            connection.setUseCaches(false);
            connection.connect();

            DataOutputStream out = new DataOutputStream(connection.getOutputStream());
            out.write(content.getBytes("UTF-8"));
            out.flush();
            out.close();

            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
            StringBuilder builder = new StringBuilder();
            String line = "";
            while ((line = reader.readLine()) != null) {
                builder.append(line);
            }
            reader.close();

            return builder.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }

        return null;
    }


    /**
     * 验证签名
     * @param jsonStr requestBody
     */
    public static void verifySign(String jsonStr) throws UnsupportedEncodingException, NoSuchAlgorithmException {
        System.out.println("接收入参：" + jsonStr);


        // 拼接签名原串(a=1&b=2)，排除签名参数
        String paramSrc = jsonStr;
        Map<String,String> param = toMap(jsonStr);
        String sendSign = param.get("s");
        System.out.println("参数中的签名：" + sendSign );
        //去除签名字段
        param.remove("s");

        //使用相同的生成签名方法
        List<String> ignoreParams = new ArrayList<>();
        ignoreParams.add("s");

        String sign = sign(param,ignoreParams,TEST_SEC);
        System.out.println("生成验证的签名：" + sign );
        //进行验签操作
        if (sign.equals(sendSign)) {
            //处理业务逻辑
            System.out.println("验签成功");

        } else {
            System.out.println("验签失败");
        }

    }


    /**
     * 模拟接收参数
     * @param paramStr
     * @return
     */
    private static Map<String,String> toMap(String paramStr) throws UnsupportedEncodingException {
        Map<String,String> param = new TreeMap<>();

        String[] paramArr = paramStr.split("&");
        for(String s :paramArr){
            String key = s.substring(0,s.lastIndexOf("="));
            String encodeStr =  s.substring(s.lastIndexOf("=") + 1);
            String value =  URLDecoder.decode(encodeStr,"UTF-8");
            param.put(key,value);
        }

        return param;
    }






}